home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
edit
/
xvisrc.zip
/
SEARCH.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-28
|
27KB
|
1,234 lines
/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)search.c 2.1 (Chris & John Downey) 7/29/92";
#endif
/***
* program name:
xvi
* function:
PD version of UNIX "vi" editor, with extensions.
* module name:
search.c
* module function:
Regular expression searching, including global command.
* history:
STEVIE - ST Editor for VI Enthusiasts, Version 3.10
Originally by Tim Thompson (twitch!tjt)
Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
Heavily modified by Chris & John Downey
***/
#include "xvi.h"
#include "regexp.h" /* Henry Spencer's regular expression routines */
#include "regmagic.h" /* Henry Spencer's regular expression routines */
#ifdef MEGAMAX
overlay "search"
#endif
/*
* String searches
*
* The actual searches are done using Henry Spencer's regular expression
* library.
*/
/*
* Names of values for the P_regextype enumerated parameter.
*/
char *rt_strings[] =
{
"tags",
"grep",
"egrep",
NULL
};
/*
* Used by g/re/p to remember where we are and what we are doing.
*/
static Line *curline;
static Line *lastline;
static long curnum;
static bool_t greptype;
static Posn *bcksearch P((Xviwin *, Line *, int, bool_t));
static Posn *fwdsearch P((Xviwin *, Line *, int, bool_t));
static char *mapstring P((char **, int));
static char *compile P((char *, int, bool_t));
static char *grep_line P((void));
static long substitute P((Xviwin *, Line *, Line *, char *, char *));
/*
* Convert a regular expression to egrep syntax: the source string can
* be either tags compatible (only ^ and $ are significant), vi
* compatible or egrep compatible (but also using \< and \>)
*
* Our first parameter here is the address of a pointer, which we
* point to the closing delimiter character if we found one, otherwise
* the closing '\0'.
*/
static char *
mapstring(sp, delim)
char **sp; /* pointer to pointer to pattern string */
int delim; /* delimiter character */
{
static Flexbuf ns;
int rxtype; /* can be rt_TAGS, rt_GREP or rt_EGREP */
register enum {
m_normal, /* nothing special */
m_startccl, /* just after [ */
m_negccl, /* just after [^ */
m_ccl, /* between [... or [^... and ] */
m_escape /* just after \ */
} state = m_normal;
register char *s;
rxtype = Pn(P_regextype);
flexclear(&ns);
for (s = *sp; *s != '\0' && (*s != delim || state != m_normal); s++) {
switch (state) {
case m_normal:
switch (*s) {
case '\\':
state = m_escape;
break;
case '(': case ')': case '+': case '?': case '|':
/* egrep metacharacters */
if (rxtype != rt_EGREP)
(void) flexaddch(&ns, '\\');
(void) flexaddch(&ns, *s);
break;
case '*': case '.': case '[':
/* grep metacharacters */
if (rxtype == rt_TAGS) {
(void) flexaddch(&ns, '\\');
} else if (*s == '[') {
/* start of character class */
state = m_startccl;
}
/* fall through ... */
default:
(void) flexaddch(&ns, *s);
}
break;
case m_startccl:
case m_negccl:
(void) flexaddch(&ns, *s);
state = (*s == '^' && state == m_startccl) ? m_negccl : m_ccl;
break;
case m_ccl:
(void) flexaddch(&ns, *s);
if (*s == ']')
state = m_normal;
break;
case m_escape:
switch (*s) {
case '(': /* bracket conversion */
case ')':
if (rxtype != rt_GREP)
(void) flexaddch(&ns, '\\');
(void) flexaddch(&ns, *s);
break;
case '.': /* egrep metacharacters */
case '\\':
case '[':
case '*':
case '?':
case '+':
case '^':
case '$':
case '|':
(void) lformat(&ns, "\\%c", *s);
break;
default: /* a normal character */
if (*s != delim)
(void) flexaddch(&ns, '\\');
(void) flexaddch(&ns, *s);
}
state = m_normal;
}
}
*sp = s;
/*
* This is horrible, but the real vi does it, so ...
*/
if (state == m_escape) {
(void) lformat(&ns, "\\\\");
}
return flexgetstr(&ns);
}
/**********************************************************
* *
* Abstract type definition. *
* *
* Regular expression node, with pointer reference count. *
* *
* We need this for global substitute commands. *
* *
**********************************************************/
typedef struct {
regexp *rn_ptr;
int rn_count;
} Rnode;
/*
* Node for last successfully compiled regular expression.
*/
static Rnode *lastprogp = NULL;
/*
* Last regular expression used in a substitution.
*/
static Rnode *last_lhs = NULL;
/*
* Last rhs for a substitution.
*/
static char *last_rhs = NULL;
/*
* rn_new(), rn_delete() & rn_duplicate() perform operations on Rnodes
* which are respectively analogous to open(), close() & dup() for
* Unix file descriptors.
*/
/*
* Make a new Rnode, given a pattern string.
*/
static Rnode *
rn_new(str)
char *str;
{
Rnode *retp;
if ((retp = (Rnode *) alloc(sizeof (Rnode))) == NULL)
return NULL;
if ((retp->rn_ptr = regcomp(str)) == NULL) {
free ((char *) retp);
return NULL;
}
retp->rn_count = 1;
return retp;
}
/*
* Make a copy of an Rnode pointer & increment the Rnode's reference
* count.
*/
#define rn_duplicate(s) ((s) ? ((s)->rn_count++, (s)) : NULL)
/*
* Decrement an Rnode's reference count, freeing it if there are no
* more pointers pointing to it.
*
* In C++, this would be a destructor for an Rnode.
*/
static void
rn_delete(rp)
Rnode *rp;
{
if (rp != NULL && --rp->rn_count <= 0) {
free((char *) rp->rn_ptr);
free((char *) rp);
}
}
#if 0
/*
* Increment the reference count for the current prog,
* and return it to the caller.
*/
static Rnode *
inccount()
{
if (lastprogp != NULL) {
lastprogp->rn_count++;
}
return(lastprogp);
}
#endif
#define cur_prog() (lastprogp->rn_ptr)
/*
* Compile given regular expression from string.
*
* The opening delimiter for the regular expression is supplied; the
* end of it is marked by an unescaped matching delimiter or, if
* delim_only is FALSE, by a '\0' character. We return a pointer to
* the terminating '\0' or to the character following the closing
* delimiter, or NULL if we failed.
*
* If, after we've found a delimiter, we have an empty pattern string,
* we use the last compiled expression if there is one.
*
* The regular expression is converted to egrep syntax by mapstring(),
* which also finds the closing delimiter. The actual compilation is
* done by regcomp(), from Henry Spencer's regexp routines.
*
* If we're successful, the compiled regular expression will be
* pointed to by lastprogp->rn_ptr, & lastprogp->rn_count will be > 0.
*/
static char *
compile(pat, delimiter, delim_only)
char *pat;
int delimiter;
bool_t delim_only;
{
Rnode *progp;
if (pat == NULL) {
return(NULL);
}
/*
* If we get an empty regular expression, we just use the last
* one we compiled (if there was one).
*/
if (*pat == '\0') {
return((delim_only || lastprogp == NULL) ? NULL : pat);
}
if (*pat == delimiter) {
return((lastprogp == NULL) ? NULL : &pat[1]);
}
progp = rn_new(mapstring(&pat, delimiter));
if (progp == NULL) {
return(NULL);
}
if (*pat == '\0') {
if (delim_only) {
rn_delete(progp);
return(NULL);
}
} else {
pat++;
}
rn_delete(lastprogp);
lastprogp = progp;
return(pat);
}
Posn *
search(window, startline, startindex, dir, strp)
Xviwin *window;
Line *startline;
int startindex;
int dir; /* FORWARD or BACKWARD */
char **strp;
{
Posn *pos;
Posn *(*sfunc) P((Xviwin *, Line *, int, bool_t));
char *str;
str = compile(*strp, (dir == FORWARD) ? '/' : '?', FALSE);
if (str == NULL) {
return(NULL);
}
*strp = str;
if (dir == BACKWARD) {
sfunc = bcksearch;
} else {
sfunc = fwdsearch;
}
pos = (*sfunc)(window, startline, startindex, Pb(P_wrapscan));
return(pos);
}
/*
* Search for the given expression, ignoring regextype, without
* wrapscan & and without using the compiled regular expression for
* anything else (so 'n', 'N', etc., aren't affected). We